package org.budo.graph.repository.redis;

import java.sql.Timestamp;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import org.budo.graph.Constant;
import org.budo.graph.entity.AppGraph;
import org.budo.graph.entity.CacheGraph;
import org.budo.graph.entity.SpringGraph;
import org.budo.graph.entity.SqlGraph;
import org.budo.graph.entity.UrlGraph;
import org.budo.graph.repository.AbstractBudoGraphRepository;
import org.budo.support.freemarker.FreemarkerUtil;
import org.budo.support.java.net.util.JavaNetUtil;
import org.budo.support.lang.util.MapUtil;
import org.budo.support.lang.util.SetUtil;
import org.budo.support.spring.io.util.ResourceUtil;
import org.budo.time.Time;

import com.alibaba.druid.stat.DruidStatService;
import com.alibaba.druid.support.json.JSONUtils;

import lombok.Getter;
import lombok.Setter;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Tuple;

/**
 * @author lmw
 */
@Getter
@Setter
public class JedisBudoGraphRepository extends AbstractBudoGraphRepository {
    private JedisPool jedisPool;

    /**
     * 改为vue视图后废弃
     */
    public String readTable(String url) {
        String templateInput = ResourceUtil.classPathResourceInputStreamToString(Constant.GRAPH_TEMPLATE_PATH + "/graph-data.htm");

        Collection<Object> values = this.readTableMapList().values();

        Map<String, Object> data = MapUtil.stringObjectMap("jedisPool", jedisPool.toString(), //
                "tableData", values, //
                "RequestParameters", DruidStatService.getParameters(url));

        return FreemarkerUtil.freemarkerRender(templateInput, data);
    }

    @Override
    public String readTable_json(String url) {
        return JSONUtils.toJSONString(this.readTableMapList().values());
    }

    @Override
    public Map<String, Object> readTableMapList() {
        Map<String, Object> resultMap = new LinkedHashMap<String, Object>();

        resultMap.putAll(this._readTableMap(AppGraph.GRAPH_TYPE));
        resultMap.putAll(this._readTableMap(UrlGraph.GRAPH_TYPE));
        resultMap.putAll(this._readTableMap(CacheGraph.GRAPH_TYPE));
        resultMap.putAll(this._readTableMap(SpringGraph.GRAPH_TYPE));
        resultMap.putAll(this._readTableMap(SqlGraph.GRAPH_TYPE));

        return resultMap;
    }

    /**
     * 获取数据量统计
     */
    private Map<String, Object> _readTableMap(String graphType) {
        Jedis jedis = this.getJedisPool().getResource();

        Map<String, Object> resultMap = new HashMap<String, Object>();
        String urlPrefix = BUDO_GRAPH + LINKER + graphType;
        Set<String> urlKeys = jedis.keys(urlPrefix + LINKER + "*");
        for (String key : urlKeys) {
            Long zcount = jedis.zcount(key, Double.MIN_VALUE, Double.MAX_VALUE);

            String from = "NULL";
            Set<Tuple> min = jedis.zrangeByScoreWithScores(key, Double.MIN_VALUE, Double.MAX_VALUE, 0, 1); // 只读取一条
            from = ((long) SetUtil.firstOrNull(min).getScore()) + "";

            String to = "NULL";
            Set<Tuple> max = jedis.zrevrangeByScoreWithScores(key, Double.MAX_VALUE, Double.MIN_VALUE, 0, 1); // 只读取一条
            to = ((long) SetUtil.firstOrNull(max).getScore()) + "";

            String str = key.replace(urlPrefix + LINKER, "");
            String[] array = str.split(LINKER);
            String hostApp = array[0] + LINKER + array[1];

            Map<String, Object> mapEach = (Map<String, Object>) resultMap.get(hostApp + LINKER + graphType);
            if (null == mapEach) {
                mapEach = MapUtil.stringObjectMap("HostApp", hostApp, //
                        "Host", array[0], //
                        "ApplicationName", array[1], //
                        "Type", graphType, //
                        "From", from, //
                        "To", to, //
                        "KeyCount", zcount);

                resultMap.put(hostApp + LINKER + graphType, mapEach);
            } else {
                Long keyCount = (Long) mapEach.get("KeyCount") + zcount;
                mapEach.put("KeyCount", keyCount);

                String fromMin = Math.min(Long.valueOf((String) mapEach.get("From")), Long.valueOf(from)) + "";
                mapEach.put("From", fromMin);

                String toMax = Math.max(Long.valueOf((String) mapEach.get("To")), Long.valueOf(to)) + "";
                mapEach.put("To", toMax);
            }
        }

        jedis.close();
        return resultMap;
    }

    /**
     * 删数据
     */
    @Override
    public Long removeData(String graphType, String app, String host, Long from, Long to) {
        double start = from.doubleValue();
        double end = to.doubleValue();

        String keyPrefix = BUDO_GRAPH + LINKER + graphType + LINKER + host + LINKER + app;

        Long remCount = 0L;

        Jedis jedis = this.getJedisPool().getResource();
        Set<String> keys = jedis.keys(keyPrefix + "*");
        for (String key : keys) { // 逐个删除指定范围
            remCount += jedis.zremrangeByScore(key, start, end);
        }
        jedis.close();

        return remCount;
    }

    /**
     * 读数据
     */
    @Override
    public Map<Double, Object> readLine(String graphType, String application, String item) {
        String key = BUDO_GRAPH + LINKER + graphType + LINKER + JavaNetUtil.getHostInnerFirst_1() + LINKER + application + LINKER + item;

        Jedis jedis = this.getJedisPool().getResource();
        Set<Tuple> zrangeWithScores = jedis.zrangeWithScores(key, 0, -1);
        jedis.close();

        Map<Double, Object> map = new HashMap<Double, Object>();
        for (Tuple tuple : zrangeWithScores) {
            map.put(tuple.getScore(), tuple.getElement());
        }
        return map;
    }

    /**
     * 写数据，注意，此处传入的Map中Key的顺序和查询时表头的关系应对应
     */
    @Override
    public void writeData(String graphType, String application, String item, Map<String, String> valueMap, Timestamp now) {
        String key = BUDO_GRAPH + LINKER + graphType + LINKER + JavaNetUtil.getHostInnerFirst_1() + LINKER + application + LINKER + item;

        Double score = Double.valueOf(Time.when(now).toString(YYYY_MM_DD_HH_MM));

        Collection<String> values = valueMap.values(); // values 是数组，应该与 titles 的顺序匹配
        String member = JSONUtils.toJSONString(values); // TODO use concat

        Jedis jedis = this.getJedisPool().getResource();
        jedis.zadd(key, score, member);
        jedis.close();
    }
}
